package com.itheima.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.article.feign.ApArticleFeign;
import com.itheima.behaviour.dto.FollowBehaviourDto;
import com.itheima.common.constants.BusinessConstants;
import com.itheima.common.constants.SystemConstants;
import com.itheima.common.enums.HttpCodeEnum;
import com.itheima.common.exception.LeadNewsException;
import com.itheima.common.util.AppJwtUtil;
import com.itheima.common.util.RequestContextUtil;
import com.itheima.common.vo.LoginUserVo;
import com.itheima.common.vo.ResultVo;
import com.itheima.user.dto.LoginDto;
import com.itheima.user.dto.UserRelationDto;
import com.itheima.user.mapper.ApUserFanMapper;
import com.itheima.user.mapper.ApUserFollowMapper;
import com.itheima.user.pojo.ApUser;
import com.itheima.user.mapper.ApUserMapper;
import com.itheima.user.pojo.ApUserFan;
import com.itheima.user.pojo.ApUserFollow;
import com.itheima.user.service.ApUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * @description <p>APP用户信息 业务实现</p>
 *
 * @version 1.0
 * @package com.itheima.user.service.impl
 */
@Service
@Slf4j
public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUser> implements ApUserService {

    @Resource
    private ApUserFollowMapper apUserFollowMapper;

    @Resource
    private ApUserFanMapper apUserFanMapper;

    @Resource
    private ApArticleFeign apArticleFeign;

    @Resource
    private KafkaTemplate kafkaTemplate;

    /**
     * App端用户登录校验
     *
     * @param dto
     * @return
     */
    @Override
    public LoginUserVo loginCheck(LoginDto dto) {
        // 默认为匿名
        Long loginUserId = SystemConstants.ANONYMOUS_USER_ID;
        //10. 构建vo
        LoginUserVo vo = new LoginUserVo();
        // 载荷
        Map<String, Object> claimMaps = new HashMap<>();
        //1. 如果手机号码与密码不为空，则说明是需要校验登录。否则为匿名登录
        if(!StringUtils.isEmpty(dto.getPhone()) && !StringUtils.isEmpty(dto.getPassword())) {
            //2. 使用手机号码查询用户信息
            ApUser loginUser = query().eq("phone", dto.getPhone()).one();
            //3. 用户信息不存在则报错
            if(null == loginUser){
                throw new LeadNewsException("手机号码或密码错误!");
            }
            //4. 用户信息存在，则要校验密码
            //5. 前端的密码加盐加密
            String encryptedPwd = DigestUtils.md5DigestAsHex((dto.getPassword() + loginUser.getSalt()).getBytes());
            //6. 与数据库的密码对比
            if(!encryptedPwd.equals(loginUser.getPassword())) {
                //7. 如果不相同则报错
                throw new LeadNewsException("手机号码或密码错误!");
            }
            // 登录用户的id
            loginUserId = loginUser.getId();
            //9. 用户信息脱敏返回
            loginUser.setPassword(null);
            loginUser.setSalt(null);
            vo.setUser(loginUser);

            // 把昵称与头像存入载荷
            claimMaps.put(SystemConstants.LOGIN_USER_NAME, loginUser.getName());// 名称
            claimMaps.put(SystemConstants.LOGIN_USER_IMAGE, loginUser.getImage());// 头像地址
        }
        //8. 相同则生成token
        claimMaps.put("id", loginUserId);
        String token = AppJwtUtil.createToken(claimMaps);
        vo.setToken(token);

        return vo;
    }

    /**
     * 用户关注|取消关注
     *
     * @param dto
     */
    @Override
    @Transactional
    public void userFollow(UserRelationDto dto) {
        Long loginUserId = RequestContextUtil.getUserId();
        // 判断是否为匿名
        if(SystemConstants.ANONYMOUS_USER_ID == loginUserId){
            // 匿名
            throw new LeadNewsException(HttpCodeEnum.NEED_LOGIN);
        }

        Long authorId = dto.getAuthorId();
        //1. 通过用户id与作者id查询关注信息
        ApUserFollow apUserFollow = getUserFollowRecord(loginUserId, authorId);
        //2. 判断操作行为类型
        if(dto.getOperation() == 0) {
            //3. 如果是关注
            // 3.1 判断是否关注过，如果关注过则报错
            if(null != apUserFollow){
                throw new LeadNewsException("已经关注过了!");
            }
            // 3.2 如果没关注，则添加关注信息，添加粉丝信息
            addUserFollow(dto);
            //添加粉丝信息
            addUserFan(dto);
            // 发消息给kafka
            FollowBehaviourDto msg = new FollowBehaviourDto();
            msg.setArticleId(dto.getArticleId());
            msg.setFollowId(dto.getAuthorId());
            msg.setUserId(RequestContextUtil.getUserId());
            String json = JSON.toJSONString(msg);
            // 发送
            kafkaTemplate.send(BusinessConstants.MqConstants.FOLLOW_BEHAVIOR_TOPIC, json);
        }else {
            //4. 如果是取消关注
            //4.1 判断是否关注过，如果没有关注则报错
            if(null == apUserFollow){
                throw new LeadNewsException("还没有关注!");
            }
            //4.2 如果是关注过，则删除关注信息 同时删除粉丝信息
            deleteUserFollow(dto);
            deleteUserFan(dto);
        }
        //5. 事务控制
    }

    /**
     * 删除粉丝信息
     * @param dto
     */
    private void deleteUserFan(UserRelationDto dto) {
        LambdaQueryWrapper<ApUserFan> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ApUserFan::getFansId, RequestContextUtil.getUserId());
        lqw.eq(ApUserFan::getUserId, dto.getAuthorId());

        apUserFanMapper.delete(lqw);
    }

    /**
     * 则删除关注信息
     * @param dto
     */
    private void deleteUserFollow(UserRelationDto dto) {
        LambdaQueryWrapper<ApUserFollow> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ApUserFollow::getUserId, RequestContextUtil.getUserId())
            .eq(ApUserFollow::getFollowId, dto.getAuthorId());
        apUserFollowMapper.delete(lqw);
    }

    /**
     * 添加粉丝信息
     * @param dto
     */
    private void addUserFan(UserRelationDto dto) {
        ApUserFan pojo = new ApUserFan();
        pojo.setUserId(dto.getAuthorId());
        pojo.setFansId(RequestContextUtil.getUserId());
        pojo.setFansName(getById(pojo.getFansId()).getName());
        pojo.setLevel(0);// 正常
        pojo.setCreatedTime(LocalDateTime.now());
        pojo.setIsDisplay(1);// 可见
        pojo.setIsShieldLetter(0);// 不屏蔽
        pojo.setIsShieldComment(0); // 不屏蔽评论
        apUserFanMapper.insert(pojo);
    }

    /**
     * 添加关注信息
     * @param dto
     */
    private void addUserFollow(UserRelationDto dto) {
        ApUserFollow pojo = new ApUserFollow();
        pojo.setFollowId(dto.getAuthorId());
        pojo.setUserId(RequestContextUtil.getUserId());
        pojo.setLevel(0);// 默认为0
        pojo.setIsNotice(1);// 要通知
        pojo.setCreatedTime(LocalDateTime.now());

        // 远程调用文章微服查询作者名称
        ResultVo<String> getResult = apArticleFeign.getAuthorNameByArticleId(dto.getArticleId());
        if(!getResult.isSuccess()){
            log.error("远程调用文章微服查询作者名称失败! {},{}", dto.getArticleId(), getResult.getErrorMessage());
            throw new LeadNewsException("关注用户失败!");
        }
        String authorName = getResult.getData();
        pojo.setFollowName(authorName);

        // 保存
        apUserFollowMapper.insert(pojo);
    }

    /**
     * 通过用户id与作者id查询关注信息
     * @param loginUserId
     * @param authorId
     * @return
     */
    private ApUserFollow getUserFollowRecord(Long loginUserId, Long authorId) {
        LambdaQueryWrapper<ApUserFollow> lqw = new LambdaQueryWrapper<>();
        lqw.eq(ApUserFollow::getUserId, loginUserId)
            .eq(ApUserFollow::getFollowId, authorId);
        return apUserFollowMapper.selectOne(lqw);
    }
}
